package com.ltcode.log.starter.aspect;

import cn.hutool.json.JSONUtil;
import com.ltcode.log.starter.annotation.BizLog;
import com.ltcode.log.starter.entity.DataEntity;
import com.ltcode.log.starter.utils.LogHandleUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
public class BizLogAspect {
    private static final Logger log = LoggerFactory.getLogger(BizLogAspect.class);
    @Pointcut("@annotation(com.an.code.starter.annotation.BizLog)")
    public void pointCut() {
        System.out.println("============pointCut=========================");
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) {
        System.out.println("BizLogAspect is triggered"); // 添加日志确认切面是否被触发
        return LogHandleUtil.process(point, (beforeEntity) -> {
            MethodSignature methodSignature = beforeEntity.getMethodSignature();
            return beforeHandle(point, methodSignature);
        }, afterEntity -> {
            afterHandle(afterEntity.getProcess(), afterEntity.getDataEntity());
        }, throwableEntity -> {
            throwableHandle(throwableEntity.getThrowable(), throwableEntity.getDataEntity());
        }, endEntity -> {
            endHandle(endEntity.getMethodSignature(), endEntity.getDataEntity());
        });
    }

    private void endHandle(MethodSignature methodSignature, DataEntity dataEntity) {
        // todo 持久化处理
        System.out.println("============endHandle=========线程名称==========="+Thread.currentThread().getName()+"================"+ JSONUtil.toJsonStr(dataEntity));
    }

    private void throwableHandle(Throwable throwable, DataEntity dataEntity) {
        if (dataEntity != null) {
            dataEntity.setErrorMsg(throwable.getMessage());
            dataEntity.setRespTime(String.valueOf(new Date()));
        }
    }

    private void afterHandle(Object result, DataEntity dataEntity) {
        if (dataEntity != null) {
            dataEntity.setRespData(result != null ? "" : result.toString());
            dataEntity.setRespTime(String.valueOf(new Date()));
        }
    }

    private DataEntity beforeHandle(ProceedingJoinPoint point, MethodSignature methodSignature) {
        DataEntity dataEntity = new DataEntity();
        Method method = methodSignature.getMethod();
        BizLog bizLog = method.getAnnotation(BizLog.class);
        String[] params = bizLog.params();
        dataEntity.setBizUrl(bizLog.biz_url());
        dataEntity.setBizType(bizLog.biz_plat());
        dataEntity.setReqData(JSONUtil.toJsonStr(methodParams(point, params)));
        dataEntity.setReqTime(String.valueOf(new Date()));
        return dataEntity;
    }

    public static Map<String, Object> methodParams(JoinPoint joinPoint, String[] params) {
        Map<String, Object> data = new HashMap();
        Object[] args = joinPoint.getArgs();
        String[] paramsNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        if (args != null && args.length > 0) {
            if (params.length > 0) {
                Arrays.stream(params).forEach((param) -> {
                    for (int i = 0; i < paramsNames.length; ++i) {
                        if (param.equals(paramsNames[i]) && !(args[i] instanceof HttpServletRequest)
                                && !(args[i] instanceof HttpServletResponse)) {
                            data.put(paramsNames[i], args[i]);
                        }
                    }
                });
            } else {
                for (int i = 0; i < paramsNames.length; ++i) {
                    if (!(args[i] instanceof HttpServletRequest) && !(args[i] instanceof HttpServletResponse)) {
                        data.put(paramsNames[i], args[i]);
                    }
                }
            }
        }
        return data;
    }
}
